import "./jqueryPlugins";
import $ from "jquery";
import "../components/init.js";
import { gui_log } from "./gui_log.js";
// same, msp seems to be everywhere used from global scope
import "./msp/MSPHelper.js";
import { i18n } from "./localization.js";
import GUI, { TABS } from "./gui.js";
import { get as getConfig, set as setConfig } from "./ConfigStorage.js";
import { checkSetupAnalytics } from "./Analytics.js";
import { initializeSerialBackend } from "./serial_backend.js";
import FC from "./fc.js";
import CONFIGURATOR from "./data_storage.js";
import CliAutoComplete from "./CliAutoComplete.js";
import DarkTheme, { setDarkTheme } from "./DarkTheme.js";
import { isExpertModeEnabled } from "./utils/isExportModeEnabled.js";
import { updateTabList } from "./utils/updateTabList.js";
import * as THREE from "three";
import NotificationManager from "./utils/notifications.js";

if (typeof String.prototype.replaceAll === "undefined") {
    String.prototype.replaceAll = function (match, replace) {
        return this.replace(new RegExp(match, "g"), () => replace);
    };
}

$(document).ready(function () {
    appReady();
});

function readConfiguratorVersionMetadata() {
    // These are injected by vite. Check for undefined is needed to prevent race conditions
    CONFIGURATOR.productName =
        typeof __APP_PRODUCTNAME__ !== "undefined" ? __APP_PRODUCTNAME__ : "Betaflight Configurator";
    CONFIGURATOR.version = typeof __APP_VERSION__ !== "undefined" ? __APP_VERSION__ : "0.0.0";
    CONFIGURATOR.gitRevision = typeof __APP_REVISION__ !== "undefined" ? __APP_REVISION__ : "unknown";
}

function cleanupLocalStorage() {
    // storage quota is 5MB, we need to clean up some stuff (more info see PR #2937)
    const cleanupLocalStorageList = [
        "cache",
        "firmware",
        "https",
        "selected_board",
        "unifiedConfigLast",
        "unifiedSourceCache",
    ];

    for (const key in localStorage) {
        for (const item of cleanupLocalStorageList) {
            if (key.includes(item)) {
                localStorage.removeItem(key);
            }
        }
    }

    setConfig({ erase_chip: true }); // force erase chip on first run
}

function appReady() {
    readConfiguratorVersionMetadata();

    cleanupLocalStorage();

    i18n.init(function () {
        startProcess();

        checkSetupAnalytics(function (analyticsService) {
            analyticsService.sendEvent(analyticsService.EVENT_CATEGORIES.APPLICATION, "AppStart", {
                sessionControl: "start",
                configuratorVersion: CONFIGURATOR.getDisplayVersion(),
                gitRevision: CONFIGURATOR.gitRevision,
                productName: CONFIGURATOR.productName,
                operatingSystem: GUI.operating_system,
                language: i18n.selectedLanguage,
            });
        });

        $("a.connection_button__link").removeClass("disabled");
        $("a.firmware_flasher_button__link").removeClass("disabled");

        initializeSerialBackend();
    });

    const showNotifications = getConfig("showNotifications", false).showNotifications;
    if (showNotifications && NotificationManager.checkPermission() === "default") {
        NotificationManager.requestPermission();
    }
}

//Process to execute to real start the app
function startProcess() {
    // translate to user-selected language
    i18n.localizePage();

    gui_log(i18n.getMessage("infoVersionOs", { operatingSystem: GUI.operating_system }));
    gui_log(i18n.getMessage("infoVersionConfigurator", { configuratorVersion: CONFIGURATOR.getDisplayVersion() }));

    $("a.connection_button__link").removeClass("disabled");
    // with Vue reactive system we don't need to call these,
    // our view is reactive to model changes
    // updateTopBarVersion();

    // log webgl capability
    // it would seem the webgl "enabling" through advanced settings will be ignored in the future
    // and webgl will be supported if gpu supports it by default (canary 40.0.2175.0), keep an eye on this one
    document.createElement("canvas");

    // log library versions in console to make version tracking easier
    console.log(`Libraries: jQuery - ${$.fn.jquery}, three.js - ${THREE.REVISION}`);

    // Check if this is the first visit
    if (getConfig("firstRun").firstRun === undefined) {
        setConfig({ firstRun: true });
        import("./tabs/static_tab.js").then(({ staticTab }) => {
            staticTab.initialize("options", () => {
                setTimeout(() => {
                    // Open the options tab after a delay
                    $("#tabs .tab_options a").click();
                }, 100);
            });
        });
    }

    // Tabs
    $("#tabs ul.mode-connected li").click(function () {
        // store the first class of the current tab (omit things like ".active")
        const tabName = $(this).attr("class").split(" ")[0];

        const tabNameWithoutPrefix = tabName.substring(4);
        if (tabNameWithoutPrefix !== "cli") {
            // Don't store 'cli' otherwise you can never connect to another tab.
            setConfig({ lastTab: tabName });
        }
    });

    $("a.firmware_flasher_button__link").on("click", function () {
        if (
            $("a.firmware_flasher_button__label").hasClass("active") &&
            $("a.firmware_flasher_button__link").hasClass("active")
        ) {
            $("a.firmware_flasher_button__label").removeClass("active");
            $("a.firmware_flasher_button__link").removeClass("active");
            $("#tabs ul.mode-disconnected .tab_landing a").click();
        } else {
            $("#tabs ul.mode-disconnected .tab_firmware_flasher a").click();
            $("a.firmware_flasher_button__label").addClass("active");
            $("a.firmware_flasher_button__link").addClass("active");
        }
    });

    const ui_tabs = $("#tabs > ul");
    $("a", "#tabs > ul").click(function () {
        if ($(this).parent().hasClass("active") === false && !GUI.tab_switch_in_progress) {
            // only initialize when the tab isn't already active
            const self = this;
            const tabClass = $(self).parent().prop("class");

            const tabRequiresConnection = $(self).parent().hasClass("mode-connected");

            const tab = tabClass.substring(4);
            const tabName = $(self).text();

            if (tabRequiresConnection && !CONFIGURATOR.connectionValid) {
                gui_log(i18n.getMessage("tabSwitchConnectionRequired"));
                return;
            }

            if (GUI.connect_lock) {
                // tab switching disabled while operation is in progress
                gui_log(i18n.getMessage("tabSwitchWaitForOperation"));
                return;
            }

            if (GUI.allowedTabs.indexOf(tab) < 0 && tab === "firmware_flasher") {
                if (GUI.connected_to || GUI.connecting_to) {
                    $("a.connection_button__link").click();
                }
                // this line is required but it triggers opening the firmware flasher tab again
                $("a.firmware_flasher_button__link").click();
            } else if (GUI.allowedTabs.indexOf(tab) < 0) {
                gui_log(i18n.getMessage("tabSwitchUpgradeRequired", [tabName]));
                return;
            }

            GUI.tab_switch_in_progress = true;

            GUI.tab_switch_cleanup(function () {
                // disable active firmware flasher if it was active
                if (
                    $("a.firmware_flasher_button__label").hasClass("active") &&
                    $("a.firmware_flasher_button__link").hasClass("active")
                ) {
                    $("a.firmware_flasher_button__label").removeClass("active");
                    $("a.firmware_flasher_button__link").removeClass("active");
                }
                // disable previously active tab highlight
                $("li", ui_tabs).removeClass("active");

                // Highlight selected tab
                $(self).parent().addClass("active");

                // detach listeners and remove element data
                const content = $("#content");
                content.empty();

                // display loading screen
                $("#cache .data-loading").clone().appendTo(content);

                function content_ready() {
                    GUI.tab_switch_in_progress = false;
                }

                checkSetupAnalytics(function (analyticsService) {
                    analyticsService.sendAppView(tab);
                });

                switch (tab) {
                    case "landing":
                        import("./tabs/landing").then(({ landing }) => landing.initialize(content_ready));
                        break;
                    case "changelog":
                        import("./tabs/static_tab").then(({ staticTab }) =>
                            staticTab.initialize("changelog", content_ready),
                        );
                        break;
                    case "privacy_policy":
                        import("./tabs/static_tab").then(({ staticTab }) =>
                            staticTab.initialize("privacy_policy", content_ready),
                        );
                        break;
                    case "options":
                        import("./tabs/options").then(({ options }) => options.initialize(content_ready));
                        break;
                    case "firmware_flasher":
                        import("./tabs/firmware_flasher").then(({ firmware_flasher }) =>
                            firmware_flasher.initialize(content_ready),
                        );
                        break;
                    case "help":
                        import("./tabs/help").then(({ help }) => help.initialize(content_ready));
                        break;
                    case "auxiliary":
                        import("./tabs/auxiliary").then(({ auxiliary }) => auxiliary.initialize(content_ready));
                        break;
                    case "adjustments":
                        import("./tabs/adjustments").then(({ adjustments }) => adjustments.initialize(content_ready));
                        break;
                    case "ports":
                        import("./tabs/ports").then(({ ports }) => ports.initialize(content_ready));
                        break;
                    case "led_strip":
                        import("./tabs/led_strip").then(({ led_strip }) => led_strip.initialize(content_ready));
                        break;
                    case "failsafe":
                        import("./tabs/failsafe").then(({ failsafe }) => failsafe.initialize(content_ready));
                        break;
                    case "transponder":
                        import("./tabs/transponder").then(({ transponder }) => transponder.initialize(content_ready));
                        break;
                    case "osd":
                        import("./tabs/osd").then(({ osd }) => osd.initialize(content_ready));
                        break;
                    case "vtx":
                        import("./tabs/vtx").then(({ vtx }) => vtx.initialize(content_ready));
                        break;
                    case "power":
                        import("./tabs/power").then(({ power }) => power.initialize(content_ready));
                        break;
                    case "setup":
                        import("./tabs/setup").then(({ setup }) => setup.initialize(content_ready));
                        break;
                    case "setup_osd":
                        import("./tabs/setup_osd").then(({ setup_osd }) => setup_osd.initialize(content_ready));
                        break;
                    case "configuration":
                        import("./tabs/configuration").then(({ configuration }) =>
                            configuration.initialize(content_ready),
                        );
                        break;
                    case "pid_tuning":
                        import("./tabs/pid_tuning").then(({ pid_tuning }) => pid_tuning.initialize(content_ready));
                        break;
                    case "receiver":
                        import("./tabs/receiver").then(({ receiver }) => receiver.initialize(content_ready));
                        break;
                    case "servos":
                        import("./tabs/servos").then(({ servos }) => servos.initialize(content_ready));
                        break;
                    case "gps":
                        import("./tabs/gps").then(({ gps }) => gps.initialize(content_ready));
                        break;
                    case "motors":
                        import("./tabs/motors").then(({ motors }) => motors.initialize(content_ready));
                        break;
                    case "sensors":
                        import("./tabs/sensors").then(({ sensors }) => sensors.initialize(content_ready));
                        break;
                    case "logging":
                        import("./tabs/logging").then(({ logging }) => logging.initialize(content_ready));
                        break;
                    case "onboard_logging":
                        import("./tabs/onboard_logging").then(({ onboard_logging }) =>
                            onboard_logging.initialize(content_ready),
                        );
                        break;
                    case "cli":
                        import("./tabs/cli").then(({ cli }) => cli.initialize(content_ready));
                        break;
                    case "presets":
                        import("../tabs/presets/presets").then(({ presets }) => presets.initialize(content_ready));
                        break;

                    default:
                        console.log(`Tab not found: ${tab}`);
                }
            });
        }

        $(".tab_container").removeClass("reveal");
        $("#background").hide();
    });

    $("#tabs ul.mode-disconnected li a:first").click();

    $("#menu_btn").on("click", function () {
        $(".tab_container").toggleClass("reveal");
        $("#background").toggle();
    });

    $("#background").on("click", function () {
        $(".tab_container").removeClass("reveal");
        $("#background").hide();
    });

    $(window).on("resize", function () {
        // 575px is the mobile breakpoint defined in CSS
        if (window.innerWidth > 575) {
            $(".tab_container").removeClass("reveal");
            $("#background").hide();
        }
    });

    // listen to all input change events and adjust the value within limits if necessary
    $("#content").on("focus", 'input[type="number"]', function () {
        const element = $(this);
        const val = element.val();

        if (!isNaN(val)) {
            element.data("previousValue", parseFloat(val));
        }
    });

    $("#content").on("change", 'input[type="number"]', function () {
        const element = $(this);
        const min = parseFloat(element.prop("min"));
        const max = parseFloat(element.prop("max"));
        const step = parseFloat(element.prop("step"));

        let val = parseFloat(element.val());

        // only adjust minimal end if bound is set
        if (element.prop("min") && val < min) {
            element.val(min);
            val = min;
        }

        // only adjust maximal end if bound is set
        if (element.prop("max") && val > max) {
            element.val(max);
            val = max;
        }

        // if entered value is illegal use previous value instead
        if (isNaN(val)) {
            element.val(element.data("previousValue"));
            val = element.data("previousValue");
        }

        // if step is not set or step is int and value is float use previous value instead
        if ((isNaN(step) || step % 1 === 0) && val % 1 !== 0) {
            element.val(element.data("previousValue"));
            val = element.data("previousValue");
        }

        // if step is set and is float and value is int, convert to float, keep decimal places in float according to step *experimental*
        if (!isNaN(step) && step % 1 !== 0) {
            const decimal_places = String(step).split(".")[1].length;

            if (val % 1 === 0 || String(val).split(".")[1].length !== decimal_places) {
                element.val(val.toFixed(decimal_places));
            }
        }
    });

    $("#showlog").on("click", function () {
        let state = $(this).data("state");
        if (state) {
            setTimeout(function () {
                const command_log = $("div#log");
                command_log.scrollTop($("div.wrapper", command_log).height());
            }, 200);
            $("#log").removeClass("active");
            $("#tab-content-container").removeClass("logopen");
            $("#scrollicon").removeClass("active");
            setConfig({ logopen: false });

            state = false;
        } else {
            $("#log").addClass("active");
            $("#tab-content-container").addClass("logopen");
            $("#scrollicon").addClass("active");
            setConfig({ logopen: true });

            state = true;
        }
        $(this).text(state ? i18n.getMessage("logActionHide") : i18n.getMessage("logActionShow"));
        $(this).data("state", state);
    });

    let result = getConfig("logopen");
    if (result.logopen) {
        $("#showlog").trigger("click");
    }

    result = getConfig("expertMode").expertMode ?? false;

    const expertModeCheckbox = $('input[name="expertModeCheckbox"]');
    expertModeCheckbox.prop("checked", result).trigger("change");

    expertModeCheckbox.on("change", () => {
        const checked = expertModeCheckbox.is(":checked");

        checkSetupAnalytics(function (analyticsService) {
            analyticsService.sendEvent(analyticsService.EVENT_CATEGORIES.APPLICATION, "ExpertMode", {
                status: checked ? "On" : "Off",
            });
        });

        if (FC.FEATURE_CONFIG && FC.FEATURE_CONFIG.features !== 0) {
            updateTabList(FC.FEATURE_CONFIG.features);
        }

        if (GUI.active_tab) {
            TABS[GUI.active_tab]?.expertModeChanged?.(checked);
        }

        setConfig({ expertMode: checked });
    });

    result = getConfig("cliAutoComplete");
    CliAutoComplete.setEnabled(typeof result.cliAutoComplete === "undefined" || result.cliAutoComplete); // On by default

    result = getConfig("darkTheme");
    if (result.darkTheme === undefined || typeof result.darkTheme !== "number") {
        // sets dark theme to auto if not manually changed
        setDarkTheme(2);
    } else {
        setDarkTheme(result.darkTheme);
    }

    window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", function () {
        DarkTheme.autoSet();
    });
}

window.isExpertModeEnabled = isExpertModeEnabled;
window.appReady = appReady;
